package com.sunop.judge.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sunop.api.client.problem.ProblemClient;
import com.sunop.api.client.user.UserClient;
import com.sunop.api.model.UserRecodeVO;
import com.sunop.judge.model.Cmd;
import com.sunop.judge.model.JudgeArgs;
import com.sunop.judge.model.JudgeInput;
import com.sunop.judge.model.JudgeRun;
import com.sunop.judge.service.JudgeService;
import com.sunop.judge.util.DockerUtil;
import com.sunop.panticommon.entity.Result;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.math.BigInteger;
import java.util.Map;

@Service
@AllArgsConstructor
@Slf4j
public class JudgeServiceImpl implements JudgeService {

    private final DockerUtil dockerUtil;
    private static int sizeOfV = 1;
    private static final String[] PATH = {"PATH=/usr/bin:/bin" ,"LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8", "LANGUAGE=en_US:en"};
    private final ProblemClient problemClient;
    private final UserClient roleClient;

    public Result judge(JudgeInput judgeInput){
        JudgeArgs args = new JudgeArgs();
        BeanUtils.copyProperties(judgeInput, args);
        Map<String, BigInteger> limitMap = problemClient.selectTimeById(args.getPid());
        args.setCpuLimit(limitMap.get("time_limit").longValue());
        args.setMemoryLimit(limitMap.get("memory_limit").longValue());
        //拿output
        String output = problemClient.getOutput(args.getPid());
        JSONObject outputArray = JSONUtil.parseObj(output);
        JSONArray resultArray = new JSONArray();
        int total= outputArray.size();int passNum=0;
        StringBuilder status= new StringBuilder("status:");
        for(sizeOfV = 1; sizeOfV <= outputArray.size(); sizeOfV++){
            String value = outputArray.getStr("out"+sizeOfV);
            resultArray=run(args,sizeOfV);
            JSONObject result = resultArray.getJSONObject(0);//
            JSONObject files = result.getJSONObject("files");
            String stdout = files.getStr("stdout").trim();// 去除换行符和空格
            log.info("output"+sizeOfV+": {}", value);
            log.info("stdout: {}", stdout);
            if(stdout.equals(value)){passNum++;}
            if(!result.getStr("status").equals("Accepted")) status.append(",").append(result.getStr("status"));// 更新 status
        }
        // 获取最后一次运行的结果
        JSONObject lastResult = resultArray.getJSONObject(0);
        double accuracy = (double) passNum / total * 100;// 计算准确率
        // 构造返回结果
        JSONObject finalResult = new JSONObject();
        finalResult.putOpt("exitStatus", lastResult.get("exitStatus"));
        finalResult.putOpt("time", lastResult.get("time"));
        finalResult.putOpt("memory", lastResult.get("memory"));
        finalResult.putOpt("runTime", lastResult.get("runTime"));
        finalResult.putOpt("accuracy", String.format("%.2f", accuracy));
        finalResult.putOpt("status", status.toString());
        //额外记录
        Map<String, String> titleAndDifficulty=problemClient.getTitleAndDifficulty(args.getPid());
        UserRecodeVO userRecodeVO = new UserRecodeVO();
        BeanUtils.copyProperties(args, userRecodeVO);
        userRecodeVO.setScoreAverage((int) Math.round(accuracy));
        userRecodeVO.setRuntime((int) lastResult.get("runTime"));
        userRecodeVO.setMemory((int) lastResult.get("memory"));
        userRecodeVO.setMassage(status.toString());
        userRecodeVO.setUid(StpUtil.getLoginIdAsInt());
        userRecodeVO.setProblemTitle(titleAndDifficulty.get("title"));
        problemClient.saveRecode(userRecodeVO); // 存入user recode
        roleClient.updateProblemPoint(StpUtil.getLoginIdAsInt(), Integer.parseInt(titleAndDifficulty.get("difficulty")));
        return Result.success(finalResult);
    }



    public JSONArray run(JudgeArgs args,int i) {
        try {
            //编译程序
            Cmd compileCmd = createCompileCommand(args,i);
            JSONArray compileResult = executeDockerCommand(args.getLanguage(), compileCmd);

            if (args.getLanguage().equals("python")) {
                return compileResult;
            }
        // 运行程序
            JudgeRun runCmd = createRunCommand(args, compileResult,i);
            JSONArray finalResult = executeDockerCommand(args.getLanguage(), runCmd);
            return finalResult;
        } catch (Exception e) {
            throw new RuntimeException("运行出错", e);
        }
    }

    private Cmd createCompileCommand(JudgeArgs args ,int i) {
        Cmd cmd = new Cmd();
        BeanUtils.copyProperties(args, cmd);

        setupCommonSettings(cmd);
        log.info("dir:{}", args.getPid()-1000);
        cmd.setCopyOutDir(""+(args.getPid()-1000));
        cmd.setFiles(createStdioFiles());

        switch (args.getLanguage()) {
            case "java":
                configureJavaCompilation(cmd, args);
                break;
            case "cpp":
                configureCppCompilation(cmd, args);
                break;
            case "python":
                configurePythonExecution(cmd, args,i);
                break;
            default:
                throw new IllegalArgumentException("不支持的编程语言");
        }
        return cmd;
    }

    private void setupCommonSettings(Cmd cmd) {
        cmd.setEnv(PATH);
        cmd.setCopyOut(new String[]{"stdout", "stderr"});
    }

    private void setupCommonSettings(JudgeRun cmd) {
        cmd.setEnv(PATH);
        cmd.setCopyOut(new String[]{"stdout", "stderr"});
    }
    private JSONObject createFileDesc(String name, int maxSize) {
        return new JSONObject()
                .set("name", name)
                .set("max", maxSize);
    }
    private JSONArray createStdioFiles() {
        JSONArray stdioFiles = new JSONArray();
        stdioFiles.add(new JSONObject().set("content", ""));
        stdioFiles.add(createFileDesc("stdout", 10240));
        stdioFiles.add(createFileDesc("stderr", 10240));
        return stdioFiles;
    }

    private void configureJavaCompilation(Cmd cmd, JudgeArgs args) {
        cmd.setArgs(new String[]{"/usr/bin/javac", "Main.java"});
        cmd.setCopyIn(createCodeFile("Main.java", args.getCode()));
        cmd.setCopyOutCached(new String[]{"Main.class"});
    }

    private void configureCppCompilation(Cmd cmd, JudgeArgs args) {
        cmd.setArgs(new String[]{"/usr/bin/g++", "a.cc", "-o", "a"});
        cmd.setCopyIn(createCodeFile("a.cc", args.getCode()));
        cmd.setCopyOutCached(new String[]{"a"});
    }

    private void configurePythonExecution(Cmd cmd, JudgeArgs args,int i) {
        cmd.setArgs(new String[]{"/usr/bin/python3", "main.py"});
        cmd.setCopyIn(createCodeFile("main.py", args.getCode()));
        cmd.setCopyOutCached(new String[]{"main.py"});
        cmd.setFiles(createIOFilesWithInput(args.getPid(),i));
    }

    private JSONArray createIOFilesWithInput(int pid,int i) {
        JSONObject inputFile = new JSONObject();
        inputFile.set("src", DockerUtil.cinFiles+(pid-1000)+"/"+i+".in");///tmp/sandbox/pid-1000/i.in
        log.info("src:{}", inputFile.getStr("src"));
        JSONArray result = new JSONArray();
        result.add(inputFile);
        result.add(createFileDesc("stdout", 10240));
        result.add(createFileDesc("stderr", 10240));
        return result;
    }

    private JSONObject createCodeFile(String filename, String content) {
        return new JSONObject()
                .set(filename, new JSONObject().set("content", content));
    }

    private JudgeRun createRunCommand(JudgeArgs args, JSONArray compileResult,int i) {
        JudgeRun runCmd = new JudgeRun();
        BeanUtils.copyProperties(args, runCmd);
        setupCommonSettings(runCmd);
        runCmd.setFiles(createIOFilesWithInput(args.getPid(),i));
        runCmd.setRealCpuLimit(args.getCpuLimit());
        runCmd.setStackLimit(args.getMemoryLimit());
        switch (args.getLanguage()) {
            case "java":
                configureJavaExecution(runCmd, compileResult);
                break;
            case "cpp":
                configureCppExecution(runCmd, compileResult);
                break;
        }
        log.info("Creating run command: {}", runCmd);
        return runCmd;
    }

    private void configureJavaExecution(JudgeRun runCmd, JSONArray compileResult) {
        runCmd.setArgs(new String[]{"java", "Main"});
        String fileId = parseFileId(compileResult, "Main.class");
        runCmd.setCopyIn(createFileReference("Main.class", fileId));
    }

    private void configureCppExecution(JudgeRun runCmd, JSONArray compileResult) {
        runCmd.setArgs(new String[]{"./a"});
        String fileId = parseFileId(compileResult, "a");
        runCmd.setCopyIn(createFileReference("a", fileId));
    }

    // 每次错这！！！
    private String parseFileId(JSONArray response, String filename) {
//        JSONObject fileIds = JSONUtil.parseObj(response.toString().split("\"fileIds\":")[1]);
        log.info("Response content: {}", response.toString());
        JSONObject firstItem = response.getJSONObject(0);
        JSONObject fileIds = firstItem.getJSONObject("fileIds");
        return fileIds.getStr(filename);
    }

    private JSONObject createFileReference(String filename, String fileId) {
        log.info("Creating file reference for filename: {} and fileId: {}", filename, fileId);
        return new JSONObject()
                .set(filename, new JSONObject().set("fileId", fileId));
    }

    private JSONArray executeDockerCommand(String language, Object command){
        JSONObject jsonCommand = wrapCommandObject(command);
        return dockerUtil.sandJudge(language, jsonCommand);
    }

private JSONObject wrapCommandObject(Object command) {
        String jsonStr = "{\"cmd\":[" + JSONUtil.toJsonStr(command) + "]}";
        return JSONUtil.parseObj(jsonStr, false);
    }
}
